﻿using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using iSlideTools.LocalServer.Models;
using iSlideTools.LocalServer.Options;
using Newtonsoft.Json;

namespace iSlideTools.LocalServer.Service
{
    public class TokenServer
    {
        private readonly JwtIssuerOptions _jwtOptions;

        public TokenServer(JwtIssuerOptions jwtOptions, SecretKeyOptions secretKeyOptions)
        {
            _jwtOptions = jwtOptions;
            ThrowIfInvalidOptions(jwtOptions);

            SecretKeyOptions = secretKeyOptions;
            JsonSerializerSettings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented
            };
        }

        public JsonSerializerSettings JsonSerializerSettings { get; }

        public SecretKeyOptions SecretKeyOptions { get; }

        public async Task<string> GenerateToken(ClaimsIdentity identity)
        {
            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, SecretKeyOptions.AppId),
                new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
                new Claim(JwtRegisteredClaimNames.Iat,
                    ToUnixEpochDate(_jwtOptions.IssueAt).ToString(),
                    ClaimValueTypes.Integer64),
                identity.FindFirst(AuthorizationInfo.UserClaim)
            };

            // Create the JWT security token and encode it.
            var jwt = new JwtSecurityToken(
                _jwtOptions.Issuer,
                _jwtOptions.Audience,
                claims,
                _jwtOptions.NotBefore,
                _jwtOptions.Expiration,
                _jwtOptions.SigningCredentials);

            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

            // Serialize and return the response
            var response = new
            {
                AccessToken = encodedJwt,
                ExpiresIn = (int) _jwtOptions.ValidFor.TotalSeconds
            };

            var json = JsonConvert.SerializeObject(response, JsonSerializerSettings);
            return json;
        }


        /// <summary>
        /// 验证App
        /// </summary>
        /// <param name="app">app</param>
        /// <returns>带有权限的对象</returns>
        /// <remarks>此方法为验证方法, 在正式项目中作为授权和配置权限使用, 注意与start up中的权限对应</remarks>
        public Task<ClaimsIdentity> AppValidate(ValidApp app)
        {
            if (app.AppId == SecretKeyOptions.AppId && app.AppKey == SecretKeyOptions.AppKey)
                return Task.FromResult(new ClaimsIdentity(new GenericIdentity(app.AppId, "Token"),
                    new[]
                    {
                        new Claim(AuthorizationInfo.UserClaim, AuthorizationInfo.ValidUser)
                    }));
            //验证不通过
            return Task.FromResult<ClaimsIdentity>(null);
        }

        public static long ToUnixEpochDate(DateTime date)
        {
            return (long) Math.Round((date.ToUniversalTime() -
                                      new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero))
                .TotalSeconds);
        }

        /// <summary>
        /// 验证jwt配置是否合法
        /// </summary>
        /// <param name="options">配置对象</param>
        public static void ThrowIfInvalidOptions(JwtIssuerOptions options)
        {
            if (options == null) throw new ArgumentNullException(nameof(options));

            if (options.ValidFor <= TimeSpan.Zero)
                throw new ArgumentException("Must be a non-zero TimeSpan.", nameof(JwtIssuerOptions.ValidFor));

            if (options.SigningCredentials == null)
                throw new ArgumentNullException(nameof(JwtIssuerOptions.SigningCredentials));

            if (options.JtiGenerator == null)
                throw new ArgumentNullException(nameof(JwtIssuerOptions.JtiGenerator));
        }
    }
}